package com.bmf.module.examination.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bmf.core.result.PageResponse;
import com.bmf.enums.ParticipateTypeEnum;
import com.bmf.enums.TestStatusEnum;
import com.bmf.module.examination.entity.*;
import com.bmf.module.examination.mapper.TestinfoMapper;
import com.bmf.module.examination.service.*;
import com.bmf.module.examination.vo.ExaminationTypeResultVo;
import com.bmf.module.examination.vo.TestinfoCreateVo;
import com.bmf.module.examination.vo.TestinfoResultVo;
import com.bmf.module.examination.vo.TestinfoUpdateVo;
import com.bmf.module.rbac.entity.RbacUser;
import com.bmf.module.rbac.service.OrganizationService;
import com.bmf.module.rbac.service.RbacUserRoleService;
import com.bmf.module.rbac.vo.TestinfoPageVo;
import com.bmf.utils.BeanCopyUtils;
import com.bmf.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * (Testinfo)表服务实现类
 *
 * @author makejava
 * @since 2023-01-03 15:49:43
 */
@Service("testinfoService")
public class TestinfoServiceImpl extends ServiceImpl<TestinfoMapper, Testinfo> implements TestinfoService {

    @Autowired
    private ExaminationTypeService examinationTypeService;

    @Autowired
    private TestinfoParticipateService testinfoParticipateService;

    @Autowired
    private OrganizationService organizationService;

    @Autowired
    private TestPaperService testPaperService;

    @Autowired
    private TestinfoRoleService testinfoRoleService;

    @Autowired
    private RbacUserRoleService rbacUserRoleService;

    @Autowired
    private ExaminationService examinationService;


    @Override
    public PageResponse<TestinfoResultVo> selectAll(TestinfoPageVo testinfoPageVo) {
        List<Integer> testIds = new ArrayList<>();
        RbacUser user = SecurityUtils.getLoginUser().getUser();
        //查询用户的考试
        //1.组织
        if(user.getOrganizationId()!=null){
            //获得组织id和所有父节点id集合
            List<Integer> ids = organizationService.getAllParentOrganizationIds(user.getOrganizationId());
            List<Integer> list = testinfoParticipateService.getByOrganizationId(ids);
            if(CollUtil.isNotEmpty(list)){
                testIds.addAll(list);
            }
        }
        //2.指定
        List<Integer> appointIds = testinfoParticipateService.getAppointTestIds(user.getId());
        if(CollUtil.isNotEmpty(appointIds)){
            testIds.addAll(appointIds);
        }

        //3.所有
        LambdaQueryWrapper<Testinfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Testinfo::getParticipateType, ParticipateTypeEnum.ALL.getCode());
        List<Testinfo> list = list(wrapper);
        if(CollUtil.isNotEmpty(list)){
            testIds.addAll(list.stream().map(Testinfo::getId).collect(Collectors.toList()));
        }

        //4.角色
        List<Integer> roleIds = rbacUserRoleService.getRoleIdsByUserId(user.getId());
        //4.1去除掉角色不匹配的考试id
        Map<Integer,List<Integer>> map = testinfoRoleService.getByTestinfoIds(testIds);
        if(CollUtil.isNotEmpty(map)){
            List<Integer> collect = map.entrySet().stream()
                    .filter(s -> Collections.disjoint(s.getValue(), roleIds)).map(s -> s.getKey()).collect(Collectors.toList());
            testIds.removeAll(collect);
        }

        wrapper.clear();
        wrapper.and(s->{
            if(CollUtil.isNotEmpty(testIds)){
                s.in(Testinfo::getId,testIds);
            }
            if(testinfoPageVo.getType()!=null){
                s.eq(Testinfo::getType, testinfoPageVo.getType());
            }
            s.le(Testinfo::getStartTime, LocalDateTime.now()).ge(Testinfo::getEndTime, LocalDateTime.now())
                    .eq(Testinfo::getStatus, TestStatusEnum.PUBLISH.getCode());
        });
        //如果不是创建人，只能搜索到在指定时间之内的考试
        wrapper.or(s->{
            s.eq(Testinfo::getCreateId,user.getId());
            if(testinfoPageVo.getStartTime()!=null){
                s.ge(Testinfo::getStartTime, testinfoPageVo.getStartTime());
            }
            if(testinfoPageVo.getEndTime()!=null){
                s.le(Testinfo::getEndTime, testinfoPageVo.getEndTime());
            }
            if(StrUtil.isNotBlank(testinfoPageVo.getName())){
                s.like(Testinfo::getName, testinfoPageVo.getName());
            }
        });

        Page<Testinfo> page = new Page(testinfoPageVo.getPageNo(), testinfoPageVo.getPageSize());
        page(page,wrapper);
        List<Testinfo> records = page.getRecords();
        if(CollUtil.isEmpty(records)){
            return new PageResponse<>(null, page.getTotal(),testinfoPageVo.getPageNo(),testinfoPageVo.getPageSize());
        }
        //查询所有testinfo_participate
        List<Integer> ids = records.stream().map(Testinfo::getType).collect(Collectors.toList());
        Map<Integer, ExaminationType> byIds = examinationTypeService.getByIds(ids);
        List<TestinfoResultVo> list1 = BeanCopyUtils.copyBeanList(records, TestinfoResultVo.class);
        list1.forEach(s->{
            ExaminationType examinationType = byIds.get(s.getType());
            if(null != examinationType){
                s.setTypeName(examinationType.getName());
            }
        });

        //计算考试统计信息
        this.Statistics(list1);

        //计算已经考试的次数和分数
        this.testCount(list1);

        //计算总分和及格分
        Map<Integer,Double> scoreMap = new HashMap<>();
        if(CollUtil.isNotEmpty(records)){
            scoreMap = records.stream().collect(Collectors.toMap(Testinfo::getId, s->examinationService.countTotalScore(s.getId())));
        }
        Map<Integer, Double> finalScoreMap = scoreMap;
        list1.forEach(s->{
            s.setFraction(finalScoreMap.get(s.getId()) + "/" + s.getPassScore());
        });

        return new PageResponse<>(list1, page.getTotal(),testinfoPageVo.getPageNo(),testinfoPageVo.getPageSize());
    }


    private void testCount(List<TestinfoResultVo> list1) {
        if(CollUtil.isEmpty(list1)){
            return;
        }
        List<Integer> ids = list1.stream().map(TestinfoResultVo::getId).collect(Collectors.toList());
        LambdaQueryWrapper<TestPaper> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(TestPaper::getFrequency,TestPaper::getId,TestPaper::getTestinfoId,TestPaper::getStudentId)
                .in(TestPaper::getTestinfoId, ids).eq(TestPaper::getStudentId, SecurityUtils.getUserId());
        List<TestPaper> list = testPaperService.list(wrapper);
        if(CollUtil.isEmpty(list)){
            return;
        }
        Map<Integer, TestPaper> map = list.stream().collect(Collectors.groupingBy(TestPaper::getTestinfoId, Collectors.collectingAndThen(Collectors.minBy(Comparator.comparing(TestPaper::getFrequency)), Optional::get)));

        list1.forEach(s->{
            TestPaper testPaper = map.get(s.getId());
            if(testPaper!=null){
                s.setTestCount(s.getFrequency()-testPaper.getFrequency());
                s.setScore(testPaper.getScore());
            }else{
                s.setTestCount(s.getFrequency());
            }
        });
    }

    private void Statistics(List<TestinfoResultVo> records) {
        List<Integer> testIds = records.stream().map(TestinfoResultVo::getId).collect(Collectors.toList());
        List<TestPaper> testPaperList = testPaperService.getByTestinfoIds(testIds);
        if(CollUtil.isEmpty(testPaperList)){
            return;
        }
        //根据testinfoid分类
        Map<Integer, List<TestPaper>> collect = testPaperList.stream().collect(Collectors.groupingBy(TestPaper::getTestinfoId));
        records.forEach(s->{
            List<TestPaper> testPaperList1 = collect.get(s.getId());
            if(CollUtil.isNotEmpty(testPaperList1)){
                //1.参加人次
                s.setParticipants(testPaperList1.size());
                //2.及格人次和及格率
                //2.1及格分
                Double passScore = s.getPassScore();
                //2.2计算人次和比例
                List<TestPaper> collect1 = testPaperList1.stream().filter(k -> k.getScore() >= passScore).collect(Collectors.toList());
                s.setPassedNum(collect1.size());
                if(collect1.size()!=0){
                    s.setPassRate(testPaperList1.size()/collect1.size());
                }else{
                    s.setPassRate(0);
                }
                //3.平均分
                double asDouble = testPaperList1.stream().mapToDouble(TestPaper::getScore).average().getAsDouble();
                s.setAverage(asDouble);
                //4.最高分
                Double aDouble = testPaperList1.stream().map(TestPaper::getScore).max(Comparator.comparingDouble(x -> x)).orElse(null);
                s.setHighestScore(aDouble);
                //5.最低分
                Double aDouble1 = testPaperList1.stream().map(TestPaper::getScore).min(Comparator.comparingDouble(x -> x)).orElse(null);
                s.setLowestScore(aDouble1);
            }else{
                s.setParticipants(0);
                s.setPassedNum(0);
                s.setHighestScore((double) 0);
                s.setLowestScore((double) 0);
                s.setAverage((double) 0);
            }
        })  ;
    }

    @Override
    public void delete(List<Integer> ids) {
        removeByIds(ids);
        //1.删除考试结果试卷
        testPaperService.deleteByTestinfoIds(ids);
        //2.删除角色关系表
        testinfoRoleService.deleteByTestinfoIds(ids);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(TestinfoUpdateVo testinfoUpdateVo) {
        Testinfo byId = getById(testinfoUpdateVo.getId());
        Testinfo testinfo = BeanCopyUtils.copyBean(testinfoUpdateVo, Testinfo.class);
        updateById(testinfo);
        //修改参与方式
        testinfoParticipateService.saveList(testinfoUpdateVo.getParticipateIds(),testinfoUpdateVo.getId(),byId.getParticipateType());
        //修改角色id
        testinfoRoleService.updateByTestinfo(testinfoUpdateVo.getRoleIds(),testinfoUpdateVo.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer insert(TestinfoCreateVo testinfoCreateVo) {
        Testinfo testinfo = BeanCopyUtils.copyBean(testinfoCreateVo, Testinfo.class);
        testinfo.setCreateId(SecurityUtils.getUserId());
        save(testinfo);
        if(CollUtil.isNotEmpty(testinfoCreateVo.getRoleIds())){
            List<TestinfoRole> collect = testinfoCreateVo.getRoleIds().stream().map(s -> new TestinfoRole(null, testinfo.getId(), s)).collect(Collectors.toList());
            testinfoRoleService.saveBatch(collect);
        }
        if(testinfoCreateVo.getParticipateType()!=null&&testinfoCreateVo.getParticipateType()!=ParticipateTypeEnum.ALL.getCode()){
            List<Integer> participateIds = testinfoCreateVo.getParticipateIds();
            if(CollUtil.isNotEmpty(participateIds)){
                List<TestinfoParticipate> list = participateIds.stream().map(s->new Integer(s))
                        .map(s -> new TestinfoParticipate(null, s, testinfo.getId(), testinfoCreateVo.getParticipateType()))
                        .collect(Collectors.toList());
                testinfoParticipateService.saveBatch(list);
            }
        }
        return testinfo.getId();
    }

    @Override
    public TestinfoResultVo selectOne(Serializable id) {
        Testinfo byId = getById(id);
        if(null == byId){
            return null;
        }
        TestinfoResultVo testinfoResultVo = BeanCopyUtils.copyBean(byId, TestinfoResultVo.class);
        LambdaQueryWrapper<TestinfoParticipate> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.eq(TestinfoParticipate::getTestinfoId, id);
        List<TestinfoParticipate> list2 = testinfoParticipateService.list(wrapper1);
        if(CollUtil.isNotEmpty(list2)){
            testinfoResultVo.setParticipateIds(list2.stream().map(TestinfoParticipate::getParticipateId).collect(Collectors.toList()));
        }
        testinfoResultVo.setRoleIds(testinfoRoleService.getByTestinfoId(id));
        return testinfoResultVo;
    }

    @Override
    public void deleteByExaminationIds(List<Integer> ids) {
        LambdaQueryWrapper<Testinfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(Testinfo::getExaminationId, ids);
        remove(wrapper);
    }

    @Override
    public List<ExaminationTypeResultVo> selectByTypeId(Integer id) {
        LambdaQueryWrapper<ExaminationType> wrapper = new LambdaQueryWrapper<>();
        if(id==null){
            //查找根节点
            wrapper.eq(ExaminationType::getParentId, 0);
            List<ExaminationType> list = examinationTypeService.list(wrapper);
            if(CollUtil.isNotEmpty(list)){
                return list.stream().map(s->BeanCopyUtils.copyBean(s, ExaminationTypeResultVo.class)).peek(s->s.setDisabled(true)).collect(Collectors.toList());
            }
        }
        //查找子节点和子节点成员
        wrapper.eq(ExaminationType::getParentId, id);
        List<ExaminationType> list = examinationTypeService.list(wrapper);
        List<ExaminationTypeResultVo> examinationTypeResultVos = new ArrayList<>();
        if(CollUtil.isNotEmpty(list)){
            examinationTypeResultVos = list.stream().map(s -> BeanCopyUtils.copyBean(s, ExaminationTypeResultVo.class)).peek(s -> s.setDisabled(true)).collect(Collectors.toList());
        }
        //查询考试
        LambdaQueryWrapper<Testinfo> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.eq(Testinfo::getType, id);
        List<Testinfo> list1 = list(wrapper1);
        if(CollUtil.isNotEmpty(list1)){
            List<ExaminationTypeResultVo> collect = list1.stream().map(s -> new ExaminationTypeResultVo(s.getId(), s.getName(), null, null, false, true)).collect(Collectors.toList());
            examinationTypeResultVos.addAll(collect);
        }
        return examinationTypeResultVos;
    }
}
